package it.unipg.bipod.web;

import java.sql.Timestamp;
import java.text.Format;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.Vector;

import it.unipg.bipod.applicationLogic.*;
import it.unipg.bipod.dataModel.*;

/**
 * SegretarioCorsiBean permette agli utenti di segreteria di consultare un storico di operazioni.<br>
 * E' presente la sottoclasse pubblica OperazioneBean.<br>
 * Eventuali messaggi o errori vengono segnalati mediante i metodi forniti da DefaultBean.
 * 
 * @author Lorenzo Porzi
 * @see DefaultBean
 * @see SegretarioBean
 */
public class SegretarioOperazioniBean extends DefaultBean {
	private SegretarioApplication application;
	
	// Elenco di operazioni visualizzate
	private OperazioneBean[] operazioni;
	
	// Date visualizzate
	private int giornoIniziale;
	private int meseIniziale;
	private int annoIniziale;
	private int giornoFinale;
	private int meseFinale;
	private int annoFinale;
	
	private int paginaCorrente;
	
	/**
	 * Costruisce un SegretarioOperazioniBean associato alla SegretarioApplication fornita.
	 *  
	 * @param application la SegretarioApplication da associare a questo SegretarioOperazioniBean.
	 */
	public SegretarioOperazioniBean(SegretarioApplication application) {
		super();
		
		this.application = application;
		this.operazioni = new OperazioneBean[0];
		
		Calendar now = Calendar.getInstance();
		annoFinale = now.get(Calendar.YEAR);
		meseFinale = now.get(Calendar.MONTH);
		giornoFinale = now.get(Calendar.DAY_OF_MONTH);
		
		now.add(Calendar.DATE, -2);
		annoIniziale = now.get(Calendar.YEAR);
		meseIniziale = now.get(Calendar.MONTH);
		giornoIniziale = now.get(Calendar.DAY_OF_MONTH);
		
		paginaCorrente = 1;
		
		aggiornaOperazioni();
	}
	
	public int getPaginaCorrente() {
		return paginaCorrente;
	}
	
	public void setPaginaCorrente(int paginaCorrente) {
		this.paginaCorrente = paginaCorrente;
	}
	
	public Vector<Integer> getPagine() {
		Vector<Integer> out = new Vector<Integer>();
		
		for (int i = 0; i < getNumeroPagine(); ++i)
			out.add(i + 1);
		
		return out;
	}
	
	/* ----------------------------------
	 * Setters e getters per le proprietà
	 * ---------------------------------- */
	
	public void setAnnoIniziale(int annoIniziale) {
		this.annoIniziale = annoIniziale;
	}

	public int getAnnoIniziale() {
		return annoIniziale;
	}

	public void setMeseIniziale(int meseIniziale) {
		this.meseIniziale = meseIniziale - 1;
	}

	public int getMeseIniziale() {
		return meseIniziale + 1;
	}

	public void setGiornoIniziale(int giornoIniziale) {
		this.giornoIniziale = giornoIniziale;
	}

	public int getGiornoIniziale() {
		return giornoIniziale;
	}

	public void setAnnoFinale(int annoFinale) {
		this.annoFinale = annoFinale;
	}

	public int getAnnoFinale() {
		return annoFinale;
	}

	public void setMeseFinale(int meseFinale) {
		this.meseFinale = meseFinale - 1;
	}

	public int getMeseFinale() {
		return meseFinale + 1;
	}

	public void setGiornoFinale(int giornoFinale) {
		this.giornoFinale = giornoFinale;
	}

	public int getGiornoFinale() {
		return giornoFinale;
	}
	
	/* --------------------------------
	 * Metodi per l'estrazione dei dati
	 * -------------------------------- */

	/**
	 * Recupera l'elenco delle operazioni da visualizzare.
	 * 
	 * @return un array di OperazioneBean con le operazioni da visualizzare
	 */
	public OperazioneBean[] getOperazioni() {
		if (paginaCorrente >= 1 && paginaCorrente <= getNumeroPagine())
			return getPagina(paginaCorrente); // Ritorna la pagina selezionata
		else
			return getPagina(1); // Ritorna la prima pagina
	}
	
	/**
	 * @return il numero di pagine di operazioni.
	 */
	public int getNumeroPagine() {
		return (int)Math.ceil((double)operazioni.length / PAGE_SIZE);
	}
	
	/**
	 * @return il numero di operazioni da visualizzare
	 */
	public int getNumeroOperazioni() {
		return operazioni.length;
	}
	
	/* ---------------------
	 * Metodi di navigazione
	 * --------------------- */
	
	/**
	 * Aggiorna l'elenco di operazioni visualizzato.<br>
	 * Questo metodo utilizza le seguenti proprieta' di presentation.ini:
	 * <ul>
	 *   <li><b>msgDateNonValide</b>: il messaggio visualizzato quando le date inserite non sono formalmente valide.</li>
	 *   <li><b>msgNessunaOperazione</b>: il messaggio visualizzato quando non ci sono operazioni nel periodo specificato.</li>
	 *   <li><b>msgDateOperazioniErrate</b>: il messaggio visualizzato quando le date non sono cronologicamente corrette.</li>
	 *   <li><b>msgErroreOperazioni</b>: il messaggio visualizzato quando si verifica un errore sconosciuto.</li>
	 * </ul>
	 */
	public void aggiornaOperazioni() {
		operazioni = new OperazioneBean[0];
		
		Timestamp dataIniziale = creaData(annoIniziale, meseIniziale, giornoIniziale);
		Calendar finale = Calendar.getInstance();
		finale.setTimeInMillis(creaData(annoFinale, meseFinale, giornoFinale).getTime());
		finale.set(Calendar.HOUR_OF_DAY, 23);
		finale.set(Calendar.MINUTE, 59);
		finale.set(Calendar.SECOND, 59);
		Timestamp dataFinale = new Timestamp(finale.getTimeInMillis());
		
		// Volida le date
		if (dataIniziale == null || dataFinale == null) {
			
			addWarnMessage(getSettings().getProperty("msgDateNonValide"));
			return;
		}
		
		try {
			OperazioneUtente[] opUt = application.getOperazioni(dataIniziale, dataFinale);
			
			// Incapsula le operazioni
			operazioni = new OperazioneBean[opUt.length];
			for (int i = 0; i < opUt.length; ++i)
				operazioni[i] = new OperazioneBean(opUt[i]);
			
			if (operazioni.length == 0)
				addInfoMessage(getSettings().getProperty("msgNessunaOperazione"));
			
		} catch (ApplicationException e) {
			addWarnMessage(getSettings().getProperty("msgErroreOperazioni"));
		} catch (IllegalArgumentException e) {
			addWarnMessage(getSettings().getProperty("msgDateOperazioniErrate"));
		}
	}
	
	/**
	 * OperazioneBean incapsula un oggetto OperazioneUtente.<br>
	 * Espone solamente le proprieta' necessarie all'interfaccia e formatta automaticamente la data.
	 * 
	 * @author Lorenzo Porzi
	 *
	 */
	public class OperazioneBean {
		private OperazioneUtente operazione;
		
		public OperazioneBean(OperazioneUtente operazione) {
			this.operazione = operazione;
		}
		
		public int getIdOperazione() {
			return operazione.getOperazione().getIdOperazione();
		}
		
		public String getDescrizione() {
			return operazione.getOperazione().getDescrizione();
		}
		
		public String getData() {
			Format dateFormat = new SimpleDateFormat("dd/MM/yyyy");
			Format timeFormat = new SimpleDateFormat("HH.mm");
			
			String data = dateFormat.format(operazione.getOperazione().getDataEsecuzione());
			data += " alle ";
			data += timeFormat.format(operazione.getOperazione().getDataEsecuzione());
			
			return data;
		}
		
		public String getUtente() {
			return operazione.getUtente().getNomeUtente();
		}
		
		public String getHash() {
			return operazione.getOperazione().getHash();
		}
	}
	
	/**
	 * Crea un oggetto {@link java.sql.Timestamp} a partire da anno mese e giorno.
	 * 
	 * @param anno
	 * @param mese
	 * @param giorno
	 * @return la data creata oppure {@code null} se i parametri non sono validi.
	 */
	private Timestamp creaData(int anno, int mese, int giorno) {
		Calendar calendar = GregorianCalendar.getInstance();
		
		calendar.set(Calendar.YEAR, anno);	
		
		if (mese > calendar.getActualMaximum(Calendar.MONTH) || mese < calendar.getActualMinimum(Calendar.MONTH))
			return null;		
		calendar.set(Calendar.MONTH, mese);
		
		if (giorno > calendar.getActualMaximum(Calendar.DAY_OF_MONTH) || giorno < calendar.getActualMinimum(Calendar.DAY_OF_MONTH))
			return null;		
		calendar.set(Calendar.DAY_OF_MONTH, giorno);
		
		calendar.set(Calendar.HOUR, 0);
		calendar.set(Calendar.MINUTE, 0);
		calendar.set(Calendar.SECOND, 0);
		
		return new Timestamp(calendar.getTimeInMillis());
	}
	
	private OperazioneBean[] getPagina(int pagina) {
		Vector<OperazioneBean> outVector = new Vector<OperazioneBean>(PAGE_SIZE);
		
		for (int i = (pagina - 1) * PAGE_SIZE; i < pagina * PAGE_SIZE && i < operazioni.length; ++i)
			outVector.add(operazioni[i]);
		
		OperazioneBean[] out = new OperazioneBean[outVector.size()];
		outVector.toArray(out);
		return out;
	}
}
